class PermissionableAttributesCodeGenerator
  attr_writer :attributes, :permissions, :permission_defaults
  

  
  def initialize(base)
    @base_class = base
  end
  
  # Saves permissions info in one column with a bitmask.
  def use_bitmask(permissions_field = :attributes_permissions)
    @permissions_field = permissions_field
  end
  
  # Allows you to store the permissions in a permissions table.
  def store_permissions_in(klass, options={})
    @permission_setting_model = klass
    options[:as] ||= klass.to_s.tableize
    @permissions_association = options[:as]    
  end
  
  # Allows the DB to store the attributes and permission defaults.
  def use_attributes_defined_in(klass, column_name="name", default_permission_column="default_permission_id")
    @attribute_fk = klass.to_s.underscore+"_id"
    attributes = klass.find(:all)
    @permission_defaults_hash = attributes.inject({}) do |defaults, a|
       defaults[a.send(column_name).downcase.gsub(" ","_").to_sym] = a.send(default_permission_column)
       defaults
    end
    @attribute_ids = attributes.inject({}) do |ids, a|
      ids[a.send(column_name).downcase.gsub(" ","_").to_sym] = a.id
      ids
    end
    @attributes = @permission_defaults_hash.keys.map(&:to_s)
  end
  
  # Allows the DB to store the permissions types.
  def use_permissions_defined_in(klass, column_name="name")
    @permission_type_fk = klass.to_s.underscore+"_id"
    @permissions_list_with_values = klass.find(:all).map{|p| [p.send(column_name), p.id]}
    @permissions_hash = @permissions_list_with_values.inject({}) do |hash, permission|
      hash[permission.first.downcase.gsub(" ","_").to_sym] = permission.last
      hash
    end
    @permissions = @permissions_hash.keys
  end
  
  #Generates the default permissions for the permissions_field in the database.  Bases this off of the given permission_defaults
  def default_permissions_for_permissions_field
    default = BitAccessor.new(0, number_of_bits_per_attribute)
    @attributes.each_with_index { |attribute, index| default[index] = default_for(attribute) }
    default.to_i
  end
  
  def generate
    
    @base_class.send(:const_set, "ATTRIBUTES_WITH_PERMISSIONS", @attributes)
    @base_class.send(:const_set, "ATTRIBUTE_IDS", @attribute_ids) if @attribute_ids
    @base_class.send(:const_set, "ATTRIBUTES_PERMISSION_DEFAULTS", permission_defaults_hash)
    @base_class.send(:const_set, "ATTRIBUTE_PERMISSION_VALUES", permissions_list_with_values)    
    @base_class.send(:const_set, "ATTRIBUTE_PERMISSIONS", permissions_hash)
    @base_class.send(:const_set, "ATTRIBUTE_PERMISSIONS_SYMS", permissions_hash.inject({}){|reverse, key_value| reverse[key_value.last]=key_value.first; reverse})
    @base_class.send(:const_set, "ALLOWABLE_ATTRIBUTE_PERMISSION_VALUES", permissions_hash.values.sort)
    
    @base_class.class_eval <<-end_eval, __FILE__, __LINE__ + 1
        class << self
          # Returns the default permission level for the given permissionable_attribute.
          def default_permission_for(permissionable_attribute)
            #make the default permission be 0 if no defaults are passed in
            self::ATTRIBUTES_PERMISSION_DEFAULTS[permissionable_attribute.to_sym] || self::ATTRIBUTES_PERMISSION_DEFAULTS[:all] || 0
          end
          
          def attribute_permissions
            self::ATTRIBUTE_PERMISSION_VALUES
          end

          def permissions_bound_checker(permission_value)
            if permission_value.is_a? Symbol
              unless permission_index = self::ATTRIBUTE_PERMISSIONS[permission_value]              
                error_message = "Invalid permission," + permission_value.inspect + ", passed in.  The defined permissions are: " + self::ATTRIBUTE_PERMISSIONS.inspect
                raise(ArgumentError,error_message)
              end
              return permission_index 
            end
            permission_value = permission_value.to_i
            return nil unless self::ALLOWABLE_ATTRIBUTE_PERMISSION_VALUES.include?(permission_value)
            permission_value
          end
        end
    end_eval
    
    if using_bitmask?
      @base_class.class_eval <<-end_eval, __FILE__, __LINE__ + 1

        def #{@permissions_field}_bit_accessor
          return @#{@permissions_field}_bit_accessor ||= BitAccessor.new(self.#{@permissions_field} || 0, #{number_of_bits_per_attribute})
        end
        
        def self.default_permissions
          return #{default_permissions_for_permissions_field}
        end
      end_eval
    end
    
    if using_table_permissions?
      @base_class.class_eval <<-end_eval, __FILE__, __LINE__ + 1
      
      
      def default_attribute_setting_object_for(permission_sym, attribute_id=nil)
        attribute_id ||= self.class::ATTRIBUTE_IDS[permission_sym]
        self.#{@permissions_association}.build(:#{@attribute_fk} => attribute_id, 
                                               :#{@permission_type_fk} => self.class.default_permission_for(permission_sym))
      end
      
      def attribute_setting_object_for(permission_sym)        
        attribute_id = self.class::ATTRIBUTE_IDS[permission_sym]
        self.#{@permissions_association}.detect { |permission_setting| 
          permission_setting.#{@attribute_fk} ==  attribute_id
        } || default_attribute_setting_object_for(permission_sym, attribute_id)
      end
      
      
      end_eval
    end
    
    @attributes.each_with_index do |permissionable_attribute, attribute_index|
      
      if using_bitmask?
        @base_class.class_eval <<-end_eval, __FILE__, __LINE__ + 1
          #Define the setters for the permissions.
          def #{permissionable_attribute}_permissions=(permission_value)
            default = self.class.default_permission_for(:#{permissionable_attribute})
             self.#{@permissions_field}_bit_accessor[#{attribute_index}] = self.class.permissions_bound_checker(permission_value) || default
             self.#{@permissions_field} = self.#{@permissions_field}_bit_accessor.to_i
          end
          
          #Also need to define getter since those won't be provided by the class
          def #{permissionable_attribute}_permissions
            self.#{@permissions_field}_bit_accessor[#{attribute_index}]
          end
          
        end_eval
      elsif using_table_permissions?
        @base_class.class_eval <<-end_eval, __FILE__, __LINE__ + 1
          
          #Define the setters for the permissions. Proxy to the assication objects.
          def #{permissionable_attribute}_permissions=(permission_value)
            default = self.class.default_permission_for(:#{permissionable_attribute})
            new_permission = self.class.permissions_bound_checker(permission_value) || default
            attribute_setting = attribute_setting_object_for(:#{permissionable_attribute})
            attribute_setting.#{@permission_type_fk} = new_permission 
            #Always saving to keep behaviour consistent...
            attribute_setting.save! #unless attribute_setting.new_record? # New records will be saved when object is saved.
          end
          
          #Also need to define getter to delegate to the object in the association proxy
          def #{permissionable_attribute}_permissions
            attribute_setting_object_for(:#{permissionable_attribute}).#{@permission_type_fk}
          end
          
        end_eval
      else
        @base_class.class_eval <<-end_eval, __FILE__, __LINE__ + 1
          #Define the setters for the permissions.
          def #{permissionable_attribute}_permissions=(permission_value)
            default = self.class.default_permission_for(:#{permissionable_attribute})
            super( self.class.permissions_bound_checker(permission_value) || default)
          end
        end_eval
      end
      
      #Now define the predicate helpers for the permissions of each attribute.
      @permissions.each do |permission|
        @base_class.class_eval <<-end_eval, __FILE__, __LINE__ + 1
          def #{permissionable_attribute}_#{permission}?
            self.#{permissionable_attribute}_permissions == self.class::ATTRIBUTE_PERMISSIONS[:#{permission}]
          end
          def #{permissionable_attribute}_permissions_as_sym ; self.class::ATTRIBUTE_PERMISSIONS_SYMS[#{permissionable_attribute}_permissions]   end
        end_eval
      end
      
    end 
  end

protected
  def max_permissions_value
    @permissions.size - 1
  end
  
  def number_of_bits_per_attribute
    @number_of_bits_per_attribute ||= max_permissions_value.to_s(2).size
  end
  
  def permissions_list_with_values
     return @permissions_list_with_values if @permissions_list_with_values
     value = -1
     @permissions_list_with_values = @permissions.inject([]){|m, permission| value+=1; m << [permission.to_s.humanize, value]}
  end
  
  def permission_defaults_hash
    return @permission_defaults_hash if @permission_defaults_hash
    @permission_defaults.each do |key, default|
      default = @permissions.rindex(default) if default.is_a? Symbol
      raise "Incorrect permission value for #{key.inspect}." if default.nil? || default < 0 || default > max_permissions_value
      @permission_defaults[key] = default
    end
    @permission_defaults_hash = @permission_defaults
  end
  
  def permissions_hash
    return @permissions_hash if @permissions_hash
    index = -1
    @permissions_hash ||= @permissions.inject({}){|hash, key| hash[key] = index+=1; hash}
  end
  
  
  def using_table_permissions?
    !!@permission_setting_model
  end
  
  def using_bitmask?
    !!@permissions_field
  end
  
  def default_for(attribute)
    permission_defaults_hash[attribute.to_sym] || permission_defaults_hash[:all] || 0
  end
  
end